package com.ByteDance.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
@ServerEndpoint(value = "/imserverSingle")
public class WebSocketSingleServer {

    /**
     * 静态变量，用来记录当前在线连接数，线程安全的类。
     * private修饰符表示这个变量只能在当前类内部访问。
     * static修饰符表示这是一个静态变量，即类级别的变量，所有对象共享。
     * AtomicInteger是一个原子整数类，提供了一些原子性操作方法，如增加、减少、获取当前值等。
     * new AtomicInteger(0)表示初始化onlineSessionClientCount为0。
     */
    private static AtomicInteger onlineSessionClientCount = new AtomicInteger(0);
    /**
     * 记录当前在线连接数
     *
     * 这行代码定义了一个静态的ConcurrentHashMap对象sessionMap，用于存储WebSocket会话（Session）。
     * 这个Map以String类型的键（可能是用户标识等）和Session类型的值存储了当前在线的WebSocket会话。
     * 使用ConcurrentHashMap是因为在多线程环境下需要确保线程安全性，
     * 而ConcurrentHashMap提供了线程安全的操作，适合在并发场景下使用。
     */
    public static final Map<Integer, Session> onlineSessionClientMap = new ConcurrentHashMap<>();

    /**
     * 连接uid和连接会话
     */
    private Integer userId;
    private Session session;
    /**
     * 连接建立成功调用的方法。由前端<code>new WebSocket</code>触发
     *
     * @param userId     每次页面建立连接时传入到服务端的id，比如用户id等。可以自定义。
     * @param session 与某个客户端的连接会话，需要通过它来给客户端发送消息
     */
    @OnOpen
    public void onOpen(@PathParam("user_id") Integer userId, Session session) {
        /**
         * session.getId()：当前session会话会自动生成一个id，从0开始累加的。
         */
        this.session = session;
        this.userId = userId;
        log.info("连接建立中 ==> session_id = {}， uid = {}", session.getId(), userId);
        //加入 Map中。将页面的userId和session绑定或者session.getId()与session
        onlineSessionClientMap.put(userId, session);
        //在线数加1
        onlineSessionClientCount.incrementAndGet();
        log.info("用户" + userId + "连接成功,当前在线人数为：" + onlineSessionClientCount);
    }
    /**
     * 连接关闭调用的方法。由前端<code>socket.close()</code>触发
     *
     * @param userId
     * @param session
     */
    @OnClose
    public void onClose(@PathParam("user_Id") Integer userId, Session session) {
        // 从 Map中移除
        onlineSessionClientMap.remove(userId);

        //在线数减1
        onlineSessionClientCount.decrementAndGet();
        log.info("连接关闭成功，当前在线数为：{} ==> 关闭该连接信息：session_id = {}， 用户userId = {} 退出。", onlineSessionClientCount, session.getId(), userId);
    }


    /**
     * 后台收到客户端消息后调用的方法。
     * 由前端<code>socket.send</code>触发
     * * 当服务端执行toSession.getAsyncRemote().sendText(xxx)后，前端的socket.onmessage得到监听。
     *
     * onMessage是一个消息中转站
     * 接收浏览器端socket.send发送过来的json数据
     *
     * @param message 客户端发送过来的消息
     * @param fromSession
     */
    @OnMessage
    public void onMessage(String message, Session fromSession) {
        log.info("用户消息：" + userId + "报文：" + message);
        if (StringUtils.isEmpty(message)) {
            try {
                JSONObject jsonObject = JSON.parseObject(message);
                Integer toUserId = jsonObject.getInteger("to_user_id");
                //String content = jsonObject.getString("content");
                //log.info("服务端收到客户端消息 ==> from_user_id = {}, to_user_id = {}, content = {}",userId, toUserId, content);
                //传送给对应的toUserId用户的webSocket
                if (onlineSessionClientMap.containsKey(toUserId)) {
                    sendToOne(toUserId,message);
                }else {
                    log.error("服务端给客户端发送消息 ==> toUserId = {} 不存在, message = {}", toUserId, message);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }
        /**
         * 模拟约定：如果未指定Uid信息，则群发，否则就单独发送
         */


    /**
     * 指定发送消息
     *
     * @param toUserId
     * @param message
     */
    public static void sendToOne(Integer toUserId, String message) {
        // 通过sid查询map中是否存在
        Session toSession = onlineSessionClientMap.get(toUserId);
        // 异步发送
        log.info("服务端给客户端发送消息 ==> toUserid = {}, message = {}", toUserId, message);
        toSession.getAsyncRemote().sendText(message);
        /*
        // 同步发送
        try {
            toSession.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error("发送消息失败，WebSocket IO异常");
            e.printStackTrace();
        }*/
    }

    /**
     * 发生错误调用的方法
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误: + this.userId" + "WebSocket发生错误，原因：" + error.getMessage());
        error.printStackTrace();
    }
}
